//
// Created by kevin on 3/6/17.
//

#ifndef CAFFE_POWER_LAYER_HPP
#define CAFFE_POWER_LAYER_HPP

#include <string>
#include <utility>
#include <vector>

#include "caffe/blob.hpp"
#include "caffe/common.hpp"
#include "caffe/layer.hpp"
#include "caffe/proto/caffe.pb.h"
#include "caffe/neuron_layers.hpp"

namespace caffe {
	template<typename Dtype>
	class PowerLayer : public NeuronLayer<Dtype> {
	public:
		/**
		 * @param param provides PowerParameter power_param,
		 *     with PowerLayer options:
		 *   - scale (\b optional, default 1) the scale @f$ \alpha @f$
		 *   - shift (\b optional, default 0) the shift @f$ \beta @f$
		 *   - power (\b optional, default 1) the power @f$ \gamma @f$
		 */
		explicit PowerLayer(const LayerParameter &param)
				: NeuronLayer<Dtype>(param) {}

		virtual void LayerSetUp(const vector<Blob < Dtype> *

		>& bottom,
		const vector<Blob < Dtype>*>& top);

		virtual inline const char *type() const { return "Power"; }

	protected:
		/**
		 * @param bottom input Blob vector (length 1)
		 *   -# @f$ (N \times C \times H \times W) @f$
		 *      the inputs @f$ x @f$
		 * @param top output Blob vector (length 1)
		 *   -# @f$ (N \times C \times H \times W) @f$
		 *      the computed outputs @f$
		 *        y = (\alpha x + \beta) ^ \gamma
		 *      @f$
		 */
		virtual void Forward_cpu(const vector<Blob < Dtype> *

		>& bottom,
		const vector<Blob < Dtype>*>& top);

		virtual void Forward_gpu(const vector<Blob < Dtype> *

		>& bottom,
		const vector<Blob < Dtype>*>& top);

		/**
		 * @brief Computes the error gradient w.r.t. the power inputs.
		 *
		 * @param top output Blob vector (length 1), providing the error gradient with
		 *      respect to the outputs
		 *   -# @f$ (N \times C \times H \times W) @f$
		 *      containing error gradients @f$ \frac{\partial E}{\partial y} @f$
		 *      with respect to computed outputs @f$ y @f$
		 * @param propagate_down see Layer::Backward.
		 * @param bottom input Blob vector (length 1)
		 *   -# @f$ (N \times C \times H \times W) @f$
		 *      the inputs @f$ x @f$; Backward fills their diff with
		 *      gradients @f$
		 *        \frac{\partial E}{\partial x} =
		 *            \frac{\partial E}{\partial y}
		 *            \alpha \gamma (\alpha x + \beta) ^ {\gamma - 1} =
		 *            \frac{\partial E}{\partial y}
		 *            \frac{\alpha \gamma y}{\alpha x + \beta}
		 *      @f$ if propagate_down[0]
		 */
		virtual void Backward_cpu(const vector<Blob < Dtype> *

		>& top,
		const vector<bool> &propagate_down,
		const vector<Blob < Dtype>*>& bottom);

		virtual void Backward_gpu(const vector<Blob < Dtype> *

		>& top,
		const vector<bool> &propagate_down,
		const vector<Blob < Dtype>*>& bottom);

		/// @brief @f$ \gamma @f$ from layer_param_.power_param()
		Dtype power_;
		/// @brief @f$ \alpha @f$ from layer_param_.power_param()
		Dtype scale_;
		/// @brief @f$ \beta @f$ from layer_param_.power_param()
		Dtype shift_;
		/// @brief Result of @f$ \alpha \gamma @f$
		Dtype diff_scale_;
	};
} // namespace caffe

#endif //CAFFE_POWER_LAYER_HPP
